/**********************************************************************
 *** *                                                            * ***
 *** *  Copyright (c) 2013 Harmonia Holdings Group LLC            * ***
 *** *                                                            * ***
 *** * Permission is hereby granted to use, modify, copy, and     * ***
 *** * distribute this software and its documentation for any     * ***
 *** * purpose only without profit, provided that the above       * ***
 *** * Copyright Notice appears in all copies and that both the   * ***
 *** * Copyright Notice and this Permission Notice appears in     * ***
 *** * every copy of supporting documentation.  No title to nor   * ***
 *** * ownership of the software is transferred hereby.  The name * ***
 *** * of Harmonia Holdings Group LLC shall not be used in        * ***
 *** * advertising or publicity related to the distribution of    * ***
 *** * the software without specific, written, prior permission.  * ***
 *** * This software is provided as-delivered without expressed   * ***
 *** * or implied warranty.  Harmonia Holdings Group LLC          * ***
 *** * makes no representation about the suitability of this      * ***
 *** * software for any purpose and accepts no responsibility for * ***
 *** * its use.                                                   * ***
 *** *                                                            * ***
 *** ************************************************************** ***
 *** *                                                            * ***
 *** * This program is free software; you can redistribute it     * ***
 *** * and/or modify it under the terms of the GNU General Public * ***
 *** * License as published by the Free Software Foundation;      * ***
 *** * either version 2 of the License, or (at your option) any   * ***
 *** * later version.                                             * ***
 *** *                                                            * ***
 *** * This program is distributed in the hope that it will be    * ***
 *** * useful, but WITHOUT ANY WARRANTY; without even the implied * ***
 *** * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR    * ***
 *** * PURPOSE.  See the GNU General Public License for more      * ***
 *** * details.                                                   * ***
 *** *                                                            * ***
 *** * You should have received a copy of the GNU General Public  * ***
 *** * License along with this program; if not, write to the Free * ***
 *** * Software Foundation, Inc., 51 Franklin Street, Fifth       * ***
 *** * Floor, Boston, MA 02110-1301, USA.                         * ***
 *** *                                                            * ***
 *** * For more information: http://www.gnu.org/licenses/gpl.html * ***
 *** *                                                            * ***
 **********************************************************************/
package org.etexascode.interrep.datamodel;

import java.io.Serializable;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.etexascode.interrep.datamodel.interfaces.IDistanceable;

/**
 * The vehicle model. 
 * @author ablatt
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
public class Vehicle implements Cloneable, Serializable, IDistanceable {

    /** Serial ID. */
    @XmlTransient
    private static final long serialVersionUID = 4963642334339705429L;

    /**
     * TODO: bbadillo - Which of these are require or optional?
     */
    public enum VEHICLE_TYPE {

        CAR(4), BUS(6), TRACTOR_TRAILER(11);
        /** The integer value of the vehicle type. */
        private int value = 0;

        /** Constructor. */
        private VEHICLE_TYPE(int value) {
            this.value = value;
        }

        /**
         * Gets the value of the vehicle type.
         * @return The integer value.
         */
        public int getValue() {
            return value;
        }
    }
    /** The vehicle ID. */
    @XmlElement
    private int vehicleID = 0;
    /** The X location. */
    @XmlElement
    protected double x = 0.0;
    /** The Y location. */
    @XmlElement
    protected double y = 0.0;
    /** The Z location. */
    @XmlElement
    protected double z = 0.0;
    /** The speed. */
    @XmlElement
    private double speed = 0.0;
    /** The length. */
    @XmlElement
    private double length = 0.0;
    /** The acceleration. */
    @XmlElement
    private double acceleration = 0.0;
    /** The width. */
    @XmlElement
    private double width = 0.0;
    /** The lane ID. */
    @XmlElement
    private int laneID = -1;
    /** 
     * The heading which is 0 <= heading < 360. Heading is measured clockwise from north.  
     */
    @XmlElement
    private Double heading = null;
    /** Bumper height of the vehicle. I believe the units are inches. */
    @XmlElement
    private double height = 0;
    /** The type of vehicle. */
    @XmlElement
    private VEHICLE_TYPE type = VEHICLE_TYPE.CAR;
    /** If the brake is pressed. */
    @XmlElement
    private boolean brakePressed;

    /** The string representation of a vehicle. */
    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();

        ret.append("vehicleID = ");
        ret.append(vehicleID);
        ret.append("\n");

        ret.append("x = ");
        ret.append(x);
        ret.append("\n");

        ret.append("y = ");
        ret.append(y);
        ret.append("\n");

        ret.append("z = ");
        ret.append(z);
        ret.append("\n");

        ret.append("speed = ");
        ret.append(speed);
        ret.append("\n");

        ret.append("length = ");
        ret.append(length);
        ret.append("\n");

        ret.append("width = ");
        ret.append(width);
        ret.append("\n");

        ret.append("laneID = ");
        ret.append(laneID);
        ret.append("\n");

        ret.append("heading = ");
        ret.append(heading);
        ret.append("\n");

        return ret.toString();
    }

    /** Check if two vehicles are equal. */
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Vehicle) {
            Vehicle rhs = (Vehicle) obj;
            return new EqualsBuilder().
                    append(vehicleID, rhs.vehicleID).
                    append(x, rhs.x).
                    append(y, rhs.y).
                    append(z, rhs.z).
                    append(speed, rhs.speed).
                    append(length, rhs.length).
                    append(width, rhs.width).
                    append(laneID, rhs.laneID).
                    append(heading, rhs.heading).
                    isEquals();
        }

        return false;
    }

    /**
     * HashCode operation. 
     * @return A hashcode of the data. 
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder(41, 43).
                append(vehicleID).
                append(x).
                append(y).
                append(z).
                append(speed).
                append(length).
                append(acceleration).
                append(width).
                append(laneID).
                append(heading).
                append(height).
                append(type).
                append(brakePressed).
                toHashCode();
    }

    /**
     * Gets the vehicle ID.
     * @return The vehicle ID.
     */
    public int getVehicleID() {
        return vehicleID;
    }

    /**
     * Sets the vehicle ID.
     * @param vehicleID The new vehicle ID.
     */
    public void setVehicleID(int vehicleID) {
        this.vehicleID = vehicleID;
    }

    /**
     * Gets the X coordinate.
     * @return The X coordinate.
     */
    @Override
    public double getX() {
        return x;
    }

    /**
     * Sets the X coordinate.
     * @param x The new X coordinate.
     */
    public void setX(double x) {
        this.x = x;
    }

    /**
     * Gets the Y coordinate.
     * @return The Y coordinate.
     */
    @Override
    public double getY() {
        return y;
    }

    /**
     * Sets the Y coordinate.
     * @param y The new Y coordinate.
     */
    public void setY(double y) {
        this.y = y;
    }

    /**
     * Gets the Z coordinate.
     * @return The Z coordinate.
     */
    public double getZ() {
        return z;
    }

    /**
     * Sets the Z coordinate.
     * @param z The new Z coordinate.
     */
    public void setZ(double z) {
        this.z = z;
    }

    /**
     * Gets the speed.
     * @return The speed.
     */
    public double getSpeed() {
        return speed;
    }

    /**
     * Sets the speed.
     * @param speed The new speed.
     */
    public void setSpeed(double speed) {
        this.speed = speed;
    }

    /**
     * Gets the acceleration.
     * @return The acceleration.
     */
    public double getAcceleration() {
        return acceleration;
    }

    /**
     * Sets the acceleration.
     * @param acceleration The new acceleration.
     */
    public void setAcceleration(double acceleration) {
        this.acceleration = acceleration;
    }

    /**
     * Gets the length.
     * @return The length.
     */
    public double getLength() {
        return length;
    }

    /**
     * Sets the length.
     * @param length The new length.
     */
    public void setLength(double length) {
        this.length = length;
    }

    /**
     * Gets the width.
     * @return The width.
     */
    public double getWidth() {
        return width;
    }

    /**
     * Sets the width.
     * @param width The new width.
     */
    public void setWidth(double width) {
        this.width = width;
    }

    /**
     * Gets the lane ID.
     * @return The lane ID.
     */
    public int getLaneID() {
        return laneID;
    }

    /**
     * Sets the lane ID.
     * @param laneID The new lane ID.
     */
    public void setLaneID(int laneID) {
        this.laneID = laneID;
    }

    /**
     * Gets the heading.
     * @return The heading.
     */
    public Double getHeading() {
        return heading;
    }

    /**
     * Sets the heading.
     * @param heading The new heading.
     */
    public void setHeading(Double heading) {
        this.heading = heading;
    }

    /**
     * Gets the height of the vehicle.
     * @return The height.
     */
    public double getHeight() {
        return height;
    }

    /**
     * Sets the height of the vehicle.
     * @param height The new vehicle height.
     */
    public void setHeight(double height) {
        this.height = height;
    }

    /**
     * Gets the type of vehicle.
     * @return The type of vehicle.
     */
    public VEHICLE_TYPE getType() {
        return type;
    }

    /**
     * Sets the type of vehicle.
     * @param type The new type of vehicle.
     */
    public void setType(VEHICLE_TYPE type) {
        this.type = type;
    }

    /**
     * Gets if the brake is pressed.
     * @return True/False.
     */
    public boolean isBrakePressed() {
        return brakePressed;
    }

    /**
     * Sets if the brake is pressed.
     * @param brakePressed True/False.
     */
    public void setBrakePressed(boolean brakePressed) {
        this.brakePressed = brakePressed;
    }

    /**
     * Get the heading in the form of {delta x, delta y}
     * @return {run, rise}
     */
    public double[] genRunRiseHeading() {
        double angle = (360.0 - getHeading()) - 90.0;
        return new double[]{Math.cos(angle), Math.sin(angle)};
    }

    /**
     * A deep clone of this instance.
     */
    @Override
    public Vehicle clone() {
        try {
            return (Vehicle) super.clone();
        } catch (CloneNotSupportedException ex) {
            throw new AssertionError();
        }
    }
}